#include "stdafx.h"
#include "Resource.h"
#include "Player.h"
#include "GamePlay.h"
#include "ChessBoard.h"
#include "ChessPiece.h"
#include "ChessBoardView.h"
#include "ChessPieceView.h"

GamePlay::GamePlay()
    : m_is_start(FALSE)
    , m_is_select(FALSE)
{
    m_player1.reset(new Player(1));
    m_player2.reset(new Player(2));
    m_chess_board.reset(new ChessBoard);
    m_chess_board_view.reset(new ChessBoardView);
    m_chess_piece_view.reset(new ChessPieceView);
}

GamePlay::~GamePlay()
{
}

HRESULT GamePlay::IsStart(BOOL& is_start)
{
    is_start = m_is_start;

    return S_OK;
}

HRESULT GamePlay::IsSelectChessPiece(BOOL& is_select)
{
    is_select = m_is_select;

    return S_OK;
}

HRESULT GamePlay::SetNoSelectChessPiece()
{
    m_is_select = FALSE;
    m_select_pos.row_index = INVALID_INDEX;
    m_select_pos.col_index = INVALID_INDEX;

    BOOL is_turn = FALSE;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetTurn(is_turn))
    if (is_turn)
    {
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->SetNoSelectPieces())
    }
    else
    {
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->SetNoSelectPieces())
    }

    return S_OK;
}

HRESULT GamePlay::StartGame(const HWND& hwnd)
{
    // Create chess board, player and chess pieces.
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_board.get()->Initialize())

    // Player1 foregone.
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->Initialize(m_chess_board))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->SetTurn(TRUE))

    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->Initialize(m_chess_board))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->SetTurn(FALSE))

    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_piece_view.get()->Initialize(m_player1, m_player2))

    m_is_start = TRUE;

    return S_OK;
}

HRESULT GamePlay::ControlsSelectPieces(const int x_pos, const int y_pos, const HWND& hwnd)
{
    BOOL player1_turn = FALSE;
    BOOL player2_turn = FALSE;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetTurn(player1_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->GetTurn(player2_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE(player1_turn != player2_turn)

    std::vector<ChessPiece*> vec_all_pieces;
    if (player1_turn)
    {
        // Check if mouse click in any player1's chess pieces.
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1->GetAllPieces(vec_all_pieces))
    }
    else
    {
        // Check if mouse click in any player2's chess pieces.
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2->GetAllPieces(vec_all_pieces))
    }

    int index = 0;
    for (; index < vec_all_pieces.size(); ++index)
    {
        RECT rect;
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->GetRectPos(rect))
        POINT temp_point;
        temp_point.x = x_pos;
        temp_point.y = y_pos;
        BOOL is_in = PtInRect(&rect, temp_point);
        if (is_in)
        {
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->SetSelect(TRUE))
            m_is_select = TRUE;

            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->GetChessPos(m_select_pos))
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->MoveCheck(m_select_pos))

            break;
        }
    }

    if (index == vec_all_pieces.size())
    {
        return S_FALSE;
    }

    return S_OK;
}

HRESULT GamePlay::ControlsMovePieces(const HWND& hwnd, const int x_pos, const int y_pos)
{
    BOOL player1_turn = FALSE;
    BOOL player2_turn = FALSE;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetTurn(player1_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->GetTurn(player2_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE(player1_turn != player2_turn)

    std::vector<ChessPiece*> vec_all_pieces;
    if (player1_turn)
    {
        // Check if mouse click in any player1's chess pieces.
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1->GetAllPieces(vec_all_pieces))
    }
    else
    {
        // Check if mouse click in any player2's chess pieces.
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2->GetAllPieces(vec_all_pieces))
    }

    POINT mouse_point;
    mouse_point.x = x_pos;
    mouse_point.y = y_pos;
    CHESS_POS mouse_pos;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_board.get()->CheckPointInRect(mouse_point, mouse_pos))

    BOOL is_find = FALSE;
    int index = 0;
    for (; index < vec_all_pieces.size(); ++index)
    {
        BOOL is_select = FALSE;
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->IsSelect(is_select))
        if (is_select)
        {
            std::vector<CHESS_POS> chess_can_move;
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->GetChessCanMove(chess_can_move))
            size_t size = chess_can_move.size();
            for (int index = 0; index < size; ++index)
            {
                if ((chess_can_move[index].row_index == mouse_pos.row_index)
                    && (chess_can_move[index].col_index == mouse_pos.col_index))
                {
                    is_find = TRUE;
                    break;
                }
            }
        }
        if (is_find)
        {
            break;
        }
    }

    if (index < vec_all_pieces.size())
    {
        if (player1_turn)
        {
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(KillChessPiece(hwnd, mouse_pos, e_player_1))
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_board.get()->SetPosFill(mouse_pos, e_player_1))
        }
        else
        {
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(KillChessPiece(hwnd, mouse_pos, e_player_2))
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_board.get()->SetPosFill(mouse_pos, e_player_2))
        }
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_board.get()->SetPosFill(m_select_pos, e_no_player))
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->SetChessPos(mouse_pos))
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(SetNoSelectChessPiece())
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(ChangeTurn())
        m_is_select = FALSE;
    }

    return S_OK;
}

HRESULT GamePlay::ControlsDrawing(const HWND& hwnd)
{
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_piece_view.get()->DrawChessPieces(hwnd))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_board_view.get()->DrawChessBoard(hwnd))
    BOOL is_turn = FALSE;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetTurn(is_turn))
    std::vector<ChessPiece*> vec_all_pieces;
    if (is_turn)
    {
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetAllPieces(vec_all_pieces))
    }
    else
    {
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->GetAllPieces(vec_all_pieces))
    }
    size_t size = vec_all_pieces.size();
    for (int index = 0; index < size; ++index)
    {
        BOOL is_select = FALSE;
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->IsSelect(is_select))
        if (is_select)
        {
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->DrawSelected(hwnd))
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(vec_all_pieces[index]->DrawCanMove(hwnd))
        }
    }
    EndPaint(hwnd, &ps);

    return S_OK;
}

HRESULT GamePlay::ChangeTurn()
{
    BOOL player1_turn = FALSE;
    BOOL player2_turn = FALSE;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetTurn(player1_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->GetTurn(player2_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE(player1_turn != player2_turn)
    if (player1_turn)
    {
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->SetTurn(FALSE))
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->SetTurn(TRUE))
    }
    else
    {
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->SetTurn(TRUE))
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->SetTurn(FALSE))
    }

    return S_OK;
}

HRESULT GamePlay::KillChessPiece(const HWND& hwnd, const CHESS_POS& mouse_pos, const PLAYER& fill)
{
    PLAYER player = e_no_player;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_chess_board.get()->GetChessFillByChessPos(mouse_pos, player))
    if (player == e_no_player)
    {
        return S_FALSE;
    }

    BOOL player1_turn = FALSE;
    BOOL player2_turn = FALSE;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetTurn(player1_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->GetTurn(player2_turn))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE(player1_turn != player2_turn)

    std::vector<ChessPiece*> vec_chess_pos_1;
    std::vector<ChessPiece*> vec_chess_pos_2;
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player1.get()->GetAllPieces(vec_chess_pos_1))
    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(m_player2.get()->GetAllPieces(vec_chess_pos_2))

    auto iter_turn = player1_turn ? vec_chess_pos_2.begin() : vec_chess_pos_1.begin();
    CHESS_TYPE chess_type = e_error_type;
    for (; iter_turn != (player1_turn ? vec_chess_pos_2.end() : vec_chess_pos_1.end()); ++iter_turn)
    {
        CHESS_POS chess_pos;
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED((*iter_turn)->GetChessPos(chess_pos))
        if (chess_pos.row_index == mouse_pos.row_index && chess_pos.col_index == mouse_pos.col_index)
        {
            RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED((*iter_turn)->GetChessType(chess_type))
            break;
        }
    }

    auto iter_noturn = player1_turn ? vec_chess_pos_1.begin() : vec_chess_pos_2.begin();
    for (; iter_noturn != (player1_turn ? vec_chess_pos_1.end() : vec_chess_pos_2.end()); ++iter_noturn)
    {
        CHESS_POS chess_pos;
        RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED((*iter_noturn)->GetChessPos(chess_pos))
        if (chess_pos.row_index == m_select_pos.row_index && chess_pos.col_index == m_select_pos.col_index)
        {
            if (chess_type == e_error_type)
            {
                RETURN_AND_RECORD_ERROR_MESSAGE_IF_FALSE(FALSE)
            }
            else if (chess_type == e_king)
            {
                if (player1_turn)
                {
                    MessageBox(hwnd, "Write win!", "Congratulation", MB_OK);
                }
                else
                {
                    MessageBox(hwnd, "Black win!", "Congratulation", MB_OK);
                }

                GameOver(hwnd);
                break;
            }
            else
            {
                if (player1_turn)
                {
                    vec_chess_pos_2.erase(iter_turn);
                    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(
                        m_chess_board.get()->SetPosFill(m_select_pos, e_no_player))
                    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(
                        m_chess_board.get()->SetPosFill(mouse_pos, e_player_1))
                    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(
                        m_player2->SetAllPieces(vec_chess_pos_2))
                }
                else
                {
                    vec_chess_pos_1.erase(iter_turn);
                    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(
                        m_chess_board.get()->SetPosFill(m_select_pos, e_no_player))
                    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(
                        m_chess_board.get()->SetPosFill(mouse_pos, e_player_2))
                    RETURN_AND_RECORD_ERROR_MESSAGE_IF_FAILED(
                        m_player1->SetAllPieces(vec_chess_pos_1))
                }
            }
        }
    }

    return S_OK;
}

HRESULT GamePlay::GameOver(const HWND& hwnd)
{
    PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_RESTART, 0), 0);

    return S_OK;
}